/**
 * xmlstorer.cpp is part of Gedule.
 *
 * Gedule is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Gedule is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Gedule.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "xmlstorer.h"
#include "../logging/logmanager.h"
#include "../logging/logger.h"
#include "../../gedulemgr.h"
#include "../../model/model.h"
#include "../../model/task.h"
#include "../../model/resource.h"
#include "../../model/constraints/task_contraints/lenght.h"
#include "../../model/constraints/task_contraints/neededresources.h"
#include "../../model/constraints/task_contraints/timewindow.h"
#include "../../model/constraints/resource_contraints/capacity.h"
#include "../../model/constraints/resource_contraints/unavailabilities.h"
#include "../../model/constraints/model_contraints/allowidles.h"
#include "../../model/constraints/model_contraints/maxtime.h"
#include "../../model/constraints/model_contraints/precedence.h"
#include <boost/foreach.hpp>
#include <QFile>

///////////////////////////////////////////////////////////////
XmlStorer::XmlStorer( const std::string & a_file ) :
    Storer(),
    m_mustEndModelElement( false ),
    m_mustEndTaskElement( false ),
    m_mustEndResourceElement( false ),
    m_file( a_file )
{
}

///////////////////////////////////////////////////////////////
XmlStorer::~XmlStorer()
{
}

///////////////////////////////////////////////////////////////
bool XmlStorer::Store( Model & a_model )
{
    bool ret = false;

    std::ostringstream strStream;
    strStream << "---- Saving model '" << a_model.ModelName() << "' into XML file: " << m_file << " ----";
    GET_LOG_MGR().Log( LOG_LOGGER, strStream.str(), WARN_LOG );
    strStream.str(  std::string() );

    QFile file( QString( m_file.c_str() ) );
    if ( file.open( QFile::WriteOnly ) )
    {
        m_writer.setDevice( &file );

        m_writer.writeStartDocument();

        ret = Storer::Store( a_model );

        WriteEndElementIfNeeded();

        if( m_mustEndModelElement )
            m_writer.writeEndElement();

        m_writer.writeEndDocument();

        strStream << "---- Model '" << a_model.ModelName() << "' saved into XML file: " << m_file << " ----";
        GET_LOG_MGR().Log( LOG_LOGGER, strStream.str() );
        strStream.str(  std::string() );
    }
    else
    {
        strStream << "Cannot open file " << m_file;
        GET_LOG_MGR().Log( LOG_LOGGER, strStream.str(), ERROR_LOG );
        strStream.str(  std::string() );
    }

    file.close();

    return ret;
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( Model * a_model )
{
    if( a_model )
    {
        m_writer.writeStartElement( MODEL_TAG.c_str() );
        m_writer.writeAttribute( ID_ATTRIBUTE.c_str(), a_model->ModelName().c_str() );
        m_mustEndModelElement = true;
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( TimeWindow * a_constraint )
{
    if( a_constraint )
    {
        m_writer.writeEmptyElement( CONSTRAINT_TAG.c_str() );
        m_writer.writeAttribute( TYPE_ATTRIBUTE.c_str(), TIME_WINDOW_CONSTRAINT_TYPE.c_str() );
        m_writer.writeAttribute( START_ATTRIBUTE.c_str(), QString::number( a_constraint->Start() ) );
        m_writer.writeAttribute( END_ATTRIBUTE.c_str(), QString::number( a_constraint->End() ) );
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( Lenght * a_constraint )
{
    if( a_constraint )
    {
        m_writer.writeEmptyElement( CONSTRAINT_TAG.c_str() );
        m_writer.writeAttribute( TYPE_ATTRIBUTE.c_str(), LENGHT_CONSTRAINT_TYPE.c_str() );
        m_writer.writeAttribute( LENGHT_ATTRIBUTE.c_str(), QString::number( a_constraint->GetLenght() ) );
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( NeededResources * a_constraint )
{
    if( a_constraint )
    {
        m_writer.writeEmptyElement( CONSTRAINT_TAG.c_str() );
        m_writer.writeAttribute( TYPE_ATTRIBUTE.c_str(), RESOURCES_NEEDED_CONSTRAINT_TYPE.c_str() );
        m_writer.writeAttribute( RESOURCES_NEEDED_ATTRIBUTE.c_str(), QString::number( a_constraint->GetNeededResources() ) );
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( Capacity * a_constraint )
{
    if( a_constraint )
    {
        m_writer.writeEmptyElement( CONSTRAINT_TAG.c_str() );
        m_writer.writeAttribute( TYPE_ATTRIBUTE.c_str(), CAPACITY_CONSTRAINT_TYPE.c_str() );
        m_writer.writeAttribute( CAPACITY_ATTRIBUTE.c_str(), QString::number( a_constraint->GetCapacity() ) );
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( Unavailabilities * a_constraint )
{
    if( a_constraint && !a_constraint->UnavailabilityPeriods().empty() )
    {
        const tUnavailabilityPeriods & unavailabilities = a_constraint->UnavailabilityPeriods();
        m_writer.writeStartElement( CONSTRAINT_TAG.c_str() );
        m_writer.writeAttribute( TYPE_ATTRIBUTE.c_str(), UNAVAILABILITIES_CONSTRAINT_TYPE.c_str() );
        BOOST_FOREACH( const tUnavailabilityPeriod & period, unavailabilities )
        {
            m_writer.writeEmptyElement( UNAVAILABILITY_TAG.c_str() );
            m_writer.writeAttribute( START_ATTRIBUTE.c_str(), QString::number( period.first ) );
            m_writer.writeAttribute( END_ATTRIBUTE.c_str(), QString::number( period.second ) );
        }
        m_writer.writeEndElement();
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( AllowIdles * a_constraint )
{
    WriteEndElementIfNeeded();

    if( a_constraint )
    {
        m_writer.writeEmptyElement( CONSTRAINT_TAG.c_str() );
        m_writer.writeAttribute( TYPE_ATTRIBUTE.c_str(), ALLOW_IDLE_CONSTRAINT_TYPE.c_str() );

        QString str = a_constraint->IsAllowIdles() ? TRUE.c_str() : FALSE.c_str();
        m_writer.writeAttribute( ALLOW_IDLE_ATTRIBUTE.c_str(), str );
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( MaxTime * a_constraint )
{
    WriteEndElementIfNeeded();

    if( a_constraint )
    {
        m_writer.writeEmptyElement( CONSTRAINT_TAG.c_str() );
        m_writer.writeAttribute( TYPE_ATTRIBUTE.c_str(), MAX_TIME_CONSTRAINT_TYPE.c_str() );
        m_writer.writeAttribute( MAX_TIME_ATTRIBUTE.c_str(), QString::number( a_constraint->GetMaxTime() ) );
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( Precedence * a_constraint )
{
    WriteEndElementIfNeeded();

    if( a_constraint )
    {
        m_writer.writeEmptyElement( CONSTRAINT_TAG.c_str() );
        m_writer.writeAttribute( TYPE_ATTRIBUTE.c_str(), PRECEDENCE_CONSTRAINT_TYPE.c_str() );
        m_writer.writeAttribute( BEFORE_ATTRIBUTE.c_str(), a_constraint->TaskBefore().c_str() );
        m_writer.writeAttribute( AFTER_ATTRIBUTE.c_str(), a_constraint->TaskAfter().c_str() );
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( Task * a_task )
{
    WriteEndElementIfNeeded();

    if( a_task )
    {
        m_writer.writeStartElement( TASK_TAG.c_str() );
        m_writer.writeAttribute( ID_ATTRIBUTE.c_str(), a_task->TaskName().c_str() );
        m_mustEndTaskElement = true;
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::Visit( Resource * a_resource )
{
    WriteEndElementIfNeeded();

    if( a_resource )
    {
        if( m_mustEndResourceElement )
            m_writer.writeEndElement();

        m_writer.writeStartElement( RESOURCE_TAG.c_str() );
        m_writer.writeAttribute( ID_ATTRIBUTE.c_str(), a_resource->ResourceName().c_str() );
        m_mustEndResourceElement = true;
    }
}

///////////////////////////////////////////////////////////////
void XmlStorer::WriteEndElementIfNeeded()
{
    if( m_mustEndTaskElement || m_mustEndResourceElement )
        m_writer.writeEndElement();

    m_mustEndTaskElement = false;
    m_mustEndResourceElement = false;
}
